//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/lexer/lexvec.h"
#include "sagittarius/io/io.h"
#include <string.h>

void lex_delete_action(lexaction* act) {
    /*if(act->type == LEX_ACTION_NONE || 
       act->type == LEX_ACTION_STOP || 
       act->type == LEX_ACTION_RESET)
        return;
    AT(act->type == LEX_VAR_INC);*/
    if(act->id)
        LX_FREE_POOL_BUFFER(act->id);
    //LX_FREE(act);
}

/// lex_createActChain
lex_actchain* lex_createActChain() {
    lex_actchain* c = LX_ALLOC(sizeof(lex_actchain));
    c->a = NULL;
    return c;
}

/// lex_deleteActChain
void lex_deleteActChain(lex_actchain* c) {
    size_t i;
    for(i=0; i<c->n; ++i)
        lex_delete_action(&c->a[i]);
    LX_FREE(c->a);
    LX_FREE(c);
}

/// lex_actchain_deepCopy
lex_actchain* lex_actchain_deepCopy(lex_actchain* d) {
    size_t i;
    lex_actchain* c = LX_ALLOC(sizeof(lex_actchain));
    c->a = LX_ALLOC(d->n*sizeof(lexaction));
    memcpy(c->a, d->a, d->n*sizeof(lexaction));
    c->n = d->n;
    
    for(i=0; i<c->n; ++i) {
        if(d->a[i].id)
            c->a[i].id = sg_lex_copypbuf(d->a[i].id);
    }
    return c;
}

/// lex_pushAct
void lex_pushAct(lex_actchain* c, lexaction a) {
    lexaction* tmp;
    if(c->a) {
        tmp = LX_ALLOC((c->n+1)*sizeof(lexaction));
        memcpy(tmp, c->a, c->n*sizeof(lexaction));
        LX_FREE(c->a);
        c->a = tmp;
        c->a[c->n++] = a;
    } else {
        c->n = 0;
        c->a = LX_ALLOC((c->n+1)*sizeof(lexaction));
        c->a[c->n++] = a;
    }
}

void lex_actchain_dump(int fd, lex_actchain* c, int ind_level) {
    size_t i;
    for(i=0; i<ind_level; ++i) SG_WRITE(fd, " ");
    for(i=0; i<c->n; ++i) {
        lexaction a = c->a[i];
        if(i!=0)
            SG_WRITE(fd, ", ");
        SG_WRITE(fd, "Action: ");
        switch(a.type) {
            case LEX_ACTION_NONE:
                SG_WRITE(fd, "None");
                break;
            case LEX_ACTION_STOP:
                SG_WRITE(fd, "Stop");
                break;
            case LEX_ACTION_RESET:
                SG_WRITE(fd, "Reset");
                break;
            case LEX_VAR_INC:
                LX_WRITE_BUF(fd, a.id);
                SG_WRITE(fd, "++");
                break;
            default:
                AN(0, "Unknown action");
        }
    }
}

/// create_lexvece
lexvece* create_lexvece() {
    lexvece* e = LX_ALLOC(sizeof(lexvece));
    e->id = NULL;
    e->r = NULL;
    //e->d = NULL;
    e->act = NULL;
    return e;
}

/// lexvece_deepCopy
lexvece* lexvece_deepCopy(lexvece* o) {
    lexvece* e = LX_ALLOC(sizeof(lexvece));
    //id
    if(o->exp) {
        e->id = LX_CREATE_POOL_BUF(LX_BUFFER_LEN(o->id));
        LX_COPY(e->id, o->id, LX_BUFFER_LEN(o->id));
        e->ownid = 1;
    }
    //exp
    if(o->exp) {
        e->exp = LX_CREATE_POOL_BUF(LX_BUFFER_LEN(o->exp));
        LX_COPY(e->exp, o->exp, LX_BUFFER_LEN(o->exp));
        e->ownexp = 1;
    }
    //r
    e->r = lAST_deepCopy(o->r);
    //act
    if(o->act) {
        e->act = lex_actchain_deepCopy(o->act); //TODO: copy id
        e->ownact = 1;
    } else {
        e->act = NULL;
    }
    
    return e;
}

/// destroy_lexvece
void destroy_lexvece(lexvece* e) {
    if(e->id && e->ownid)
        LX_FREE_POOL_BUFFER(e->id);
    if(e->r)
        lAST_destroy_node(e->r);
    if(e->exp && e->ownexp)
        LX_FREE_POOL_BUFFER(e->exp);
    //if(e->d)
        //LX_FREE(e->d);
    if(e->act && e->ownact)
        lex_deleteActChain(e->act);
    LX_FREE(e);
}

/// init_lexvec
void init_lexvec(lexvec* v) {
    v->n = 0;
    v->e = NULL;
    v->d = NULL;
}

lexvec* create_lexvec() {
    lexvec* v = LX_ALLOC(sizeof(lexvec));
    init_lexvec(v);
    return v;
}

/// destroy_lexvec
void destroy_lexvec(lexvec* v) {
    size_t n;
    for(n=0; n<v->n; ++n) {
        destroy_lexvece(v->e[n]);
    }
    if(v->e)
        LX_FREE(v->e);
#if !LX_USE_POOLALLOC
    if(v->d)
        re_destroyDC(v->d);
#endif
    LX_FREE(v);
}

/// lexvec_computeDCe
void lexvec_computeDCe(lexvece* e) {
    if(!e->r->d)
        lex_computeDC(e->r);
}

/// lexvec_dumpElt
void lexvec_dumpElt(int fd, lexvece* e, int ind_level) {
    size_t i;
    
    if(e->id) {
        // leading indent
        for(i=0; i<ind_level; ++i) SG_WRITE(fd, " ");
        SG_WRITE(fd, "ID: ");
        LX_WRITE_BUF(fd, e->id);
        SG_WRITE(fd, "\n");
    }
    
    AT(e->r);
    lex_writeOut(fd, e->r, ind_level+2);
    
    if(e->act) {
        lex_actchain_dump(fd, e->act, ind_level);
        SG_WRITE(fd, "\n");
    }
}

/// lexvec_push
void lexvec_push(lexvec* v, lexvece* e) {
    lexvece** tmp = LX_ALLOC((v->n+1)*sizeof(lexvece*));
    memcpy(tmp, v->e, (v->n)*sizeof(lexvece*));
    tmp[v->n] = e;
    LX_FREE(v->e);
    v->e = tmp;
    ++v->n;
}

/// lexvec_computeDC
void lexvec_computeDC(lexvec* v) {
    size_t n;
    RE_DerivClass* d=NULL;
    AT(v->n > 0, "Cannot compute DC for empty vector");
#if LX_USE_POOLALLOC
    lexvec_computeDCe(v->e[0]);
    v->d = v->e[0]->r->d;
#else
    lexvec_computeDCe(v->e[0]);
    v->d = re_deepcopyDC(v->e[0]->r->d);
#endif
//     SG_WRITE(1, "  DC of element: ");
//     re_writeDC(1, v->e[0]->r->d);
//     SG_WRITE(1, "\n");
    
    for(n=1; n<v->n; ++n) {
        lexvec_computeDCe(v->e[n]);
//         SG_WRITE(1, "  DC of element: ");
//         re_writeDC(1, v->e[n]->r->d);
//         SG_WRITE(1, "\n");
        d = re_DCMesh(v->d, v->e[n]->r->d);
        #if !LX_USE_POOLALLOC
            re_destroyDC(v->d);
        #endif
        v->d = d;
    }
//     SG_WRITE(1, "DC of vector: ");
//     re_writeDC(1, v->d);
//     SG_WRITE(1, "\n");
}

/// lexvec_compEq
int lexvec_compEq(lexvec* u, lexvec* v) {
    size_t n;
    if(u->n != v->n)
        return 0;
    for(n=0; n<u->n; ++n) {
        if(lex_compare(u->e[n]->r, v->e[n]->r) != LEX_COMPARE_EQ)
            return 0;
    }
    return 1;
}

int lexvec_isNull(lexvec* v) {
    size_t n;
    for(n=0; n<v->n; ++n) {
        if(!lex_isNull(v->e[n]->r))
            return 0;
    }
    return 1;
}

/// lexvec_elidePassive
lexvec* lexvec_elidePassive(lexvec* v) {
    lexvec* r = create_lexvec();
    size_t n;
    for(n=0; n<v->n; ++n) {
        if(v->e[n]->act)
            lexvec_push(r, lexvece_deepCopy(v->e[n]));
    }
    return r;
}

void lexvec_dump(int fd, lexvec* v, int ind_level) {
    size_t n, i;
    for(n=0; n<v->n; ++n) {
        for(i=0; i<ind_level; ++i) SG_WRITE(2, " ");
        SG_WRITE(fd, "Element ");
        sg_writeUInt64(fd, n);
        SG_WRITE(fd, ":\n");
        lexvec_dumpElt(fd, v->e[n], ind_level+2);
    }
}
